package com.elinshaw.pattern.latch;

import java.util.concurrent.TimeUnit;

/**
 * @author Administrator
 */
public class CountDownLatch extends Latch {

    private Runnable runnable;

    public CountDownLatch(int limit) {
        super(limit);
    }

    /**
     * 添加回调
     *
     * @param limit
     * @param runnable
     */
    public CountDownLatch(int limit, Runnable runnable) {
        super(limit);
        this.runnable = runnable;
    }


    @Override
    public void await() throws InterruptedException {
        synchronized (this) {
            while (limit > 0) {
                this.await();
            }
        }
        if (runnable != null) {
            runnable.run();
        }
    }

    @Override
    public void await(TimeUnit timeUnit, long time) throws InterruptedException, WaitTimeOutException {
        if (time < 0) {
            throw new IllegalArgumentException("the time is invalid");
        }
        long remainingNanos = timeUnit.toNanos(time);
        long endNanos = System.nanoTime() + remainingNanos;
        synchronized (this) {
            while (limit > 0) {
                if (TimeUnit.NANOSECONDS.toMillis(remainingNanos) <= 0) {
                    throw new WaitTimeOutException("The wait time over specify time");
                }
                //等待remainingNanos在等待过程中可能被中断需要重新计算remainingNanos
                this.wait(TimeUnit.NANOSECONDS.toMillis(remainingNanos));
                remainingNanos = endNanos - System.nanoTime();
            }
        }
        if (runnable != null) {
            runnable.run();
        }
    }

    @Override
    public void countDown() {
        synchronized (this) {
            if (limit < 0) {
                throw new IllegalStateException("all of task already arrived");
            }
            limit--;
            this.notifyAll();
        }
    }

    @Override
    public int unArrived() {
        return this.limit;
    }

}
